//
//  MarketDataAnalyzerAppDelegate.m
//  MarketDataAnalyzer
//
//  Created by jiro on 10/01/18.
//  Copyright 2010 jiro music. All rights reserved.
//

#import "MarketDataAnalyzerAppDelegate.h"
#import "../DbTypes/DbConstants.h"
#import "../Common/MarketJaDbOperator.h"
#import "../DbTypes/DbTypes.h"
#import "CalcCorrelationCoefficientThreadArg.h"
#import "DataAnalyzeArg.h"
#import "Model/Model000.h"
#import <cmath>

const NSString* kText_CalclatorExePath = @"/Users/jiro/Development/MarketAnalyzer/Executables/CorrelationCoefficientsCalculater";

@implementation MarketDataAnalyzerAppDelegate

@synthesize window;

- (id) init
{
	self = [ super init ];

	dbInfo = [ MarketDatabase dbInfo ];

	progressIndicator = [ [ NSProgressIndicator alloc ] init ];

	return self;
}

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
	NSLog( @"adf" );
	// Insert code here to initialize your application 
//	[ startDatePicker setDateValue: [ NSDate dateWithTimeIntervalSinceNow: - 3600 * 24 * 365 ] ];
}

- (void) setUiEnableProcessing: (BOOL) yes
{
	[ calc2BrandsCorrelationCoefficientButton setEnabled: ! yes ];
	[ cancelButton setEnabled: yes ];
	if ( yes ) {
		[ progressIndicator startAnimation: self ];
	}
	else {
		[ progressIndicator stopAnimation: self ];	
	}
}

- (IBAction) calc2BrandsCorrelationCoefficient: (id) sender
{
	workingThread = [ [ NSThread alloc ] initWithTarget: self selector: @selector( calc2BrandsCorrelationCoefficientThreadFunc: ) object: nil ];
	[ workingThread start ];
	[ self setUiEnableProcessing: YES ];
}

- (IBAction) addCorrelationCoefficientCalcScgedule: (id) sender
{
//	{
//		//	---- スケジュールを追加 ---- //
//		StockBrandJaList* brandCodes_ = [ MarketJaDbOperator allBrandCodes: YES ];
//		NSEnumerator* e = [ brandCodes_.instance objectEnumerator ];
//		StockBrandJa* stockBrandJa;
//		DbOperator* dbOperator = [ DbOperator defaultOperator ];
//		while ( stockBrandJa = [ e nextObject ] )
//		{
//			NSLog( @"%@", stockBrandJa.brandCode );
//			for ( int i = 6; i < 7; i++ )
//			{
//				for ( int j = 4; j < 5; j++ )
//				{
//					PGresult* res;
//					NSString* startString = [ NSString stringWithFormat: @"20%02d-%d-1 00:00:00+0900", i, j + 1 ];
//					NSString* endString = [ NSString stringWithFormat: @"20%02d-%d-1 00:00:00+0900", i + 1, j + 1 ];
//					NSString* query = [ NSString stringWithFormat:
//						@"insert into %@ values( %d, '%@', '%@', '%@', '%@', %@, '0000', false );\n",  
//						kText_AnalyzeScheduleTable,
//						Analyze_CorrelationCoefficientOf2StockBrandInTheSamePeriod,
//						startString, endString, startString, endString,
//						stockBrandJa.brandCode
//					];
//	//				NSLog( @"%@", query );
//					if ( DbResponse_Success != [ dbOperator executeQuery: query intoResult: & res ] )
//					{
//						[ messageTextBox setStringValue: [ NSString stringWithFormat: @"SQL実行に失敗\n%@\nsql:%@", [ dbOperator errorMessage ], query ] ];
//					}
//					PQclear( res );
//					NSLog( @"%@: %@", stockBrandJa.brandCode, startString );
//				}
//			}
//		}
//		[ dbOperator disconnect ];
//		//	------------------------ //
//	}
//	return;

	NSDate* start = [ startDatePicker dateValue ];
	NSDate* end = [ NSDate dateWithTimeInterval: 3600 * 24 * 365 * ( (float)[ monthInterval intValue ] / 12 ) sinceDate: start ];
	NSMutableString* startString = [ NSMutableString stringWithString: [ start descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil ] ];
	NSMutableString* endString = [ NSMutableString stringWithString: [ end  descriptionWithCalendarFormat: @"%Y-%m-%d" timeZone: nil locale: nil ] ];
	
	[ startString appendString: @" 00:00:00+09" ];
	[ endString appendString: @" 00:00:00+09" ];
	
	DbOperator* dbOperator = [ [ DbOperator alloc ] init ];
	[ dbOperator connectToDb: kText_DefaultDb ofHost: kText_DefaultHost asUser:kText_DefaultUserName ];

	//	---- 銘柄コードを取得 ---- //
	NSString* query = [ NSString stringWithFormat:
		@"select * from %@ where %@ = true;\n",
		kText_StockBrandJaTable, kText_ExistFlag ];

	PGresult* res;
	if ( DbResponse_Success != [ dbOperator executeQuery: query intoResult: & res ] )
	{
		[ messageTextBox setStringValue: [ NSString stringWithFormat: @"SQL実行に失敗\n%@\nsql:%@", [ dbOperator errorMessage ], query ] ];
		return;
	}

	NSMutableArray* brandCodes = [ [ NSMutableArray alloc ] init ];
	int rowCount = PQntuples( res );
	int columnCount = PQnfields( res );
	for ( int i = 0; i < rowCount; i++ )
	{
		NSMutableArray* values = [ [ NSMutableArray alloc ] init ];
		for ( int j = 0; j < columnCount; j++ )
		{
			[ values addObject: [ NSString stringWithCString: PQgetvalue( res , i, j ) encoding: NSUTF8StringEncoding ] ];
		}
		[ brandCodes addObject: [ StockBrandJa stockBrandJaWithDbResponse: values ] ];
	}
	PQclear( res );
	//	----------------------- //

	//	---- スケジュールを追加 ---- //
	NSEnumerator* e = [ brandCodes objectEnumerator ];
	StockBrandJa* stockBrandJa;
	while ( stockBrandJa = [ e nextObject ] )
	{
		query = [ NSString stringWithFormat:
			@"insert into %@ values( %d, '%@', '%@', '%@', '%@', %@, '0000', false );\n",  
			kText_AnalyzeScheduleTable,
			Analyze_CorrelationCoefficientOf2StockBrandInTheSamePeriod,
			startString, endString, startString, endString,
			stockBrandJa.code
		];
		if ( DbResponse_Success != [ dbOperator executeQuery: query intoResult: & res ] )
		{
			[ messageTextBox setStringValue: [ NSString stringWithFormat: @"SQL実行に失敗\n%@\nsql:%@", [ dbOperator errorMessage ], query ] ];
		}
		PQclear( res );

	}
	//	------------------------ //

	[ dbOperator disconnect ];
}

- (void) calc2BrandsCorrelationCoefficientThreadFunc: (id) arg
{
	[ messageTextBox setStringValue: @"前処理中" ];

	//	---- 変数定義 ---- //
	int rowCount = -1;
	int columnCount = -1;
	NSMutableArray* stockBrandCodes = [ [ NSMutableArray alloc ] init ];
	DbOperator* dbOperator = [ [ DbOperator alloc ] init ];
	[ dbOperator connectToDb: kText_DefaultDb ofHost: kText_DefaultHost asUser: kText_DefaultUserName ];
	NSString* sqlQuery;
	PGresult* res = nil;
	//	---------------- //

	//	---- 銘柄コードを取得 ---- //
	sqlQuery = [ NSString stringWithFormat: 
		@"select * from %@ where %@ = true;",
		kText_StockBrandJaTable, kText_ExistFlag ];
	if ( DbResponse_Success != [ dbOperator executeQuery: sqlQuery intoResult: & res ] )
	{
		[ messageTextBox setStringValue: 
			[ NSString stringWithFormat: @"銘柄コードの取得に失敗\n%@", [ dbOperator errorMessage ] ] ];
		[ dbOperator disconnect ];
		return;
	}
	rowCount = PQntuples( res );
	columnCount = PQnfields( res );
	for ( int i = 0; i < rowCount; i++ )
	{
		NSMutableArray* values = [ [ NSMutableArray alloc ] init ];
		for ( int j = 0; j < columnCount; j++ )
		{
			[ values addObject: [ NSString stringWithCString: PQgetvalue( res, i, j ) encoding: NSUTF8StringEncoding ] ];
		}
		[ stockBrandCodes addObject: [ StockBrandJa stockBrandJaWithDbResponse: values ] ];
	}	
	PQclear( res );
	//	---------------------- //

	//	---- 分析スケジュールの日付を取得 ---- //
	NSMutableArray* startDates = [ [ NSMutableArray alloc ] init ];
	NSMutableArray* endDates = [ [ NSMutableArray alloc ] init ];
	sqlQuery = [ NSString stringWithFormat:
		@"select distinct %@, %@ from %@ where %@ = %d;",
		kText_OptionDate1, kText_OptionDate2, kText_AnalyzeScheduleTable, kText_AnalyzeType, Analyze_CorrelationCoefficientOf2StockBrandInTheSamePeriod
	];
	if ( DbResponse_Success != [ dbOperator executeQuery: sqlQuery intoResult: & res ] )
	{
		[ messageTextBox setStringValue: 
			[ NSString stringWithFormat: @"分析スケジュールの取得に失敗\n%@", [ dbOperator errorMessage ] ] ];
		[ dbOperator disconnect ];
		return;
	}	
	rowCount = PQntuples( res );
	columnCount = PQnfields( res );
	for ( int i = 0; i < rowCount; i++ )
	{
		[ startDates 
			addObject: [ NSDate dateWithString: [ DbUtility convertIsoStdDateStringToCocoaDateString: 
					[ NSString stringWithCString: PQgetvalue( res, i, 0 ) encoding: NSUTF8StringEncoding ] ] ] ];
		[ endDates
			addObject: [ NSDate dateWithString: [ DbUtility convertIsoStdDateStringToCocoaDateString: 
					[ NSString stringWithCString: PQgetvalue( res, i, 1 ) encoding: NSUTF8StringEncoding ] ] ] ];
	}
	PQclear( res );
	//	-------------------------------- //


	//	---- タスクプールを生成 ---- //
	NSMutableArray* tasks = [ [ NSMutableArray alloc ] init ];
	for ( int j = 0; j < dbServerCrocessorCoreCount; j++ )
	{
		[ tasks addObject: [ [ NSTask alloc ] init ] ];
	}
	//	------------------------ //

	for ( int dateIndex = 0; dateIndex < [ startDates count ]; dateIndex++ )
	{
		NSDate* startDate = [ startDates objectAtIndex: dateIndex ];
		NSDate* endDate = [ endDates objectAtIndex: dateIndex ];

		//	---- 分析スケジュールを取得 ---- //
		NSMutableArray* schedules = [ [ NSMutableArray alloc ] init ];
		sqlQuery = [ NSString stringWithFormat: 
			@"select * from %@ where %@ = %d and %@ = '%@' and %@ = '%@' order by %@",
			kText_AnalyzeScheduleTable, 
			kText_AnalyzeType, Analyze_CorrelationCoefficientOf2StockBrandInTheSamePeriod,
			kText_OptionDate1, [ startDate description ],
			kText_OptionDate2, [ endDate description ],
			kText_OptionString1 ];
		if ( DbResponse_Success != [ dbOperator executeQuery: sqlQuery intoResult: & res ] )
		{
			[ messageTextBox 
				setStringValue: [ NSString stringWithFormat: @"分析スケジュールの取得に失敗\n%@", [ dbOperator errorMessage ] ] ];
//			[ dbOperator disconnect ];
			continue;
		}
		rowCount = PQntuples( res );
		columnCount = PQnfields( res );
		for ( int i = 0; i < rowCount; i++ )
		{
			NSMutableArray* values = [ [ NSMutableArray alloc ] init ];
			for ( int j = 0; j < columnCount; j++ )
			{
				[ values addObject: [ NSString stringWithCString: PQgetvalue( res, i, j ) encoding: NSUTF8StringEncoding ] ];
			}
			[ schedules addObject: [ AnalyzeSchedule analyzeScheduleWithDbRespose: values ] ];
		}
		PQclear( res );
		//	--------------------------- //
		
		//	---- 仕事を分割、実行 ---- //
		int maxBrandCodeIndex = 0;
		int scheduleIndex = 0;
		while ( YES )
		{
			if ( isCancel == YES ) 
			{
				break;
			}
			if ( scheduleIndex > [ schedules count ] - 1 )
			{
				break;
			}
			for ( int taskIndex = 0; taskIndex < [ tasks count ]; taskIndex++ )
			{
				NSTask* task = [ tasks objectAtIndex: taskIndex ];
				if ( NO == [ task isRunning ] )
				{
					task = [ [ NSTask alloc ] init ];
					[ tasks replaceObjectAtIndex: taskIndex withObject: task ];
					
					//	---- 一定の仕事量を切り分ける ---- //
					int taskAmount = 0;
					NSMutableArray* brandCodes = [ [ NSMutableArray alloc ] init ];
					for ( ; scheduleIndex < [ schedules count ]; scheduleIndex++ )
					{
						AnalyzeSchedule* schedule = [ schedules objectAtIndex: scheduleIndex ];
						for ( int i = maxBrandCodeIndex; i < [ stockBrandCodes count ]; i++ )
						{
							StockBrandJa* code = [ stockBrandCodes objectAtIndex: i ];
							if ( YES == [ code.code isEqualToString: schedule.optionString1 ] )
							{
								[ brandCodes addObject: code.code ];
								maxBrandCodeIndex = i + 1;
								break;
							}
						}
						taskAmount += [ stockBrandCodes count ] - maxBrandCodeIndex;
						if ( taskAmount > 10000 )
						{
							scheduleIndex++;
							break;
						}
					}
					//	----------------------------- //


					NSMutableArray* args = [ [ NSMutableArray alloc ] init ];
					[ args addObject: @"-start_date" ];
					[ args addObject: [ startDate description ] ];
					[ args addObject: @"-end_date" ];
					[ args addObject: [ endDate description ] ];
					[ args addObject: @"-brand_codes" ];
					[ args addObject: [ NSString stringWithFormat: @"%d", [ brandCodes count ] ] ];
					for ( int i = 0; i < [ brandCodes count ]; i++ )
					{
						[ args addObject: [ brandCodes objectAtIndex: i ] ];
					}
					[ task setArguments: args ];
					[ task setLaunchPath: kText_CalclatorExePath ];
					[ task launch ];
					
					[ messageTextBox 
						setStringValue: 
							[ NSString stringWithFormat: @"%@ ~ %@", [ startDate description ], [ brandCodes objectAtIndex: 0 ] ] ];
				}
			}
			[ NSThread sleepForTimeInterval: 1 ];
		}
		//	---------------------- //
	}

	[ self setUiEnableProcessing: NO ];
}

- (IBAction) cancel: (id) sender
{
	[ messageTextBox setStringValue: 
		[ NSString stringWithFormat: @"%@\nキャンセル中...", [ messageTextBox stringValue ] ] ];
	isCancel = YES;
}

- (IBAction) freeButton: (id) sender
{
//	Model000* model = [ [ Model000 alloc ] init ];
//	[ model exec ];
}

- (void) calcCorrelationCoefficientThreadFunc: (id) argument
{
	CalcCorrelationCoefficientThreadArg* arg = (CalcCorrelationCoefficientThreadArg*) argument;

	DataAnalyzer* analyzer =
		[ [ DataAnalyzer alloc ] 
			initWithConnectionToDb: kText_DefaultDb ofHost: kText_DefaultHost asUser: kText_DefaultUserName andDbInfo: dbInfo ];

	ExpressionNode* expr = [ [ ExpressionNode alloc ] init ];
	ExpressionNode* lhs = [ [ ExpressionNode alloc ] init ];
	ExpressionNode* rhs = [ [ ExpressionNode alloc ] init ];
	lhs.type = ExpressionNode_Value;
	lhs.value = kText_EndPrice;
	rhs.type = ExpressionNode_Value;
	rhs.value = kText_AdjustRate;
	expr.type = ExpressionNode_Multiple;
	expr.lhs = lhs;
	expr.rhs = rhs;

	StockBrandJa* brand_1 = [ arg.stockBrandJas objectAtIndex: arg.brandCodeIndex1 ];
	DataAnalyzeArg* dataAnalyzeArg1 = [ [ DataAnalyzeArg alloc ] init ];
	dataAnalyzeArg1.tableId = Table_StockExchangeJaTable;
	dataAnalyzeArg1.dataFieldId = Field_Invalid;
	dataAnalyzeArg1.dataFieldNode = expr;
	dataAnalyzeArg1.conditionFieldId = Field_BrandCode;
	dataAnalyzeArg1.condition = brand_1.code;
	dataAnalyzeArg1.rangeConditionFieldId = Field_ExchangeDate;
	dataAnalyzeArg1.fromCondition = [ arg.startDate description ];
	dataAnalyzeArg1.toCondition = [ arg.endDate description ];
	
	NSMutableString* sqlQuery = [ [ NSMutableString alloc ] initWithFormat:
		@"insert into %@ values ", kText_StockCorrelationCoefficientJaTable ];
	int brandCodeCount = [ arg.stockBrandJas count ];
	BOOL firstValue = YES;
	for ( int i = arg.brandCodeIndex1 + 1; i < brandCodeCount; i++ )
	{
		StockBrandJa* brand_2 = [ arg.stockBrandJas objectAtIndex: i ];
		DataAnalyzeArg* dataAnalyzeArg2 = [ [ DataAnalyzeArg alloc ] init ];
		dataAnalyzeArg2.tableId = Table_StockExchangeJaTable;
		dataAnalyzeArg2.dataFieldId = Field_Invalid;
		dataAnalyzeArg2.dataFieldNode = expr;
		dataAnalyzeArg2.conditionFieldId = Field_BrandCode;
		dataAnalyzeArg2.condition = brand_2.code;
		dataAnalyzeArg2.rangeConditionFieldId = Field_ExchangeDate;
		dataAnalyzeArg2.fromCondition = [ arg.startDate description ];
		dataAnalyzeArg2.toCondition = [ arg.endDate description ];

		double coefficient;
		double count;
		BOOL res;
		res = [ analyzer correlationCoefficientOf: dataAnalyzeArg1 arg: dataAnalyzeArg2 andJoinFieldId: Field_ExchangeDate intoValue: & coefficient ];
		if ( NO == res )
		{
			NSLog( @"相関係数の計算に失敗 %@ %@", dataAnalyzeArg1.condition, dataAnalyzeArg2.condition );
			NSLog( @"%@", [ [ analyzer dbOperator ] errorMessage ] );
		}
		if ( - 0.750f < coefficient && coefficient < 0.750f )
		{
			continue;
		}
		res = [ analyzer countOf: dataAnalyzeArg1 arg: dataAnalyzeArg2 andJoinFieldId: Field_ExchangeDate intoValue: & count ];
		if ( NO == res )
		{
			NSLog( @"データ件数の計算に失敗 %@ %@", dataAnalyzeArg1.condition, dataAnalyzeArg2.condition );
			NSLog( @"%@", [ [ analyzer dbOperator ] errorMessage ] );
		}
		if ( 60 > count )
		{
			continue;
		}
		if ( NO == firstValue )	{
			[ sqlQuery appendString: @", " ];
		} else {
			firstValue = NO;
		}

		[ sqlQuery appendFormat: @"( '%@', '%@', '%@', '%@', '%@', '%@', %1.0f, %1.12f )\n",
			dataAnalyzeArg1.condition, dataAnalyzeArg1.fromCondition, dataAnalyzeArg1.toCondition,
			dataAnalyzeArg2.condition, dataAnalyzeArg2.fromCondition, dataAnalyzeArg2.toCondition,
			count, coefficient ];
	}

	NSRange range = [ sqlQuery rangeOfString: @"(" ];
	if ( range.location != NSNotFound ) {

		[ sqlQuery appendString: @";" ];

		PGresult* res;
		if ( DbResponse_Success != [ [ analyzer dbOperator ] executeQuery: sqlQuery intoResult: & res ] )
		{
			PQclear( res );
			NSLog( @"データの挿入に失敗 %@", dataAnalyzeArg1.condition );
			NSLog( @"%@", [ [ analyzer dbOperator ] errorMessage ] );
	//		NSLog( @"%@", sqlQuery );
		}
		else
		{
			PQclear( res );
			
			//	---- ログを更新---- //
			sqlQuery = [ NSString stringWithFormat: 
				@"update %@ set %@ = '%@' where %@ = %d and %@ = '%@' and %@ = '%@';",
				kText_AnalyzeScheduleTable,
				kText_OptionString1, ((StockBrandJa*)[ arg.stockBrandJas objectAtIndex: arg.brandCodeIndex1 ]).code,
				kText_AnalyzeType, Analyze_CorrelationCoefficientOf2StockBrandInTheSamePeriod,
				kText_OptionDate1, [ arg.startDate description ],
				kText_OptionDate2, [ arg.endDate description ] ];
	//			NSLog( @"%@", sqlQuery );
			if ( DbResponse_Success != [ [ analyzer dbOperator ] executeQuery: sqlQuery intoResult: & res ] )
			{
				[ messageTextBox setStringValue: [ [ analyzer dbOperator ] errorMessage ] ];
				NSLog( @"データの挿入に成功したがログの書き込みに失敗\n%@", [ [ analyzer dbOperator ] errorMessage ] );
			}
			PQclear( res );
			//	----------------- //
		}
	}
	
	[ [ analyzer dbOperator ] disconnect ];
//	[ messageTextBox setStringValue:
//		[ NSString stringWithFormat: @"%@\n銘柄コード: %@ の所要時間: %f [sec]",
//		[ messageTextBox stringValue ], 
//		dataAnalyzeArg1.condition,
//		[ end timeIntervalSinceDate: start ] ] ];
}

@end
